﻿using LightCAD.MathLib;
using LightCAD.Three;
using System;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Reactive.Joins;
using System.Xml.Schema;
using GeometryData = LightCAD.Model.GeometryData;

namespace QdArch
{
    public class RampAction : ComponentInstance2dAction
    {
        private PointInputer PointInputer { get; set; }
        private QdRampInstance _rampInstance = null;
        private Vector2 StartPoint;
        private Vector2 CenterPoint;
        public RampAction() { }
        public RampAction(IDocumentEditor docEditor) : base(docEditor) 
        {
            this.commandCtrl.WriteInfo("命令：Ramp");
        }
        public async void ExecCreate(string[] args = null)
        {
            this.StartCreating();
            this.PointInputer = new PointInputer(this.docEditor);
            _rampInstance = new QdRampInstance(ComponentManager.GetCptDef<QdRampRef>("坡道", "坡道", RampAttribute.BuiltinUuid));
            _rampInstance.SlabThickness = 100;
            _rampInstance.RampWidth = 1000;
            _rampInstance.ElevationStart = 0;
            _rampInstance.ElevationEnd = 1000;
            var result = await this.PointInputer.Execute("请选择坡道起点或[弧线坡(A)/放弃(U)]:");
            if (result == null || this.PointInputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }
            if(result.ValueX != null  && result.ValueX is Vector2)
            {
                StartPoint = result.ValueX as Vector2;
                _rampInstance.Position.Set(StartPoint.X, StartPoint.Y);
                _rampInstance.BaseLine = new Line2d(new Vector2(), StartPoint.Clone());
                var result2 = await this.PointInputer.Execute("请选择坡道终点:");
                if (result2 == null || result2.ValueX == null || !(result2.ValueX is Vector2) || this.PointInputer.isCancelled)
                {
                    this.Cancel();
                    goto End;
                }
                var endPoint = result2.ValueX as Vector2;
                _rampInstance.BaseLine = new Line2d(new Vector2(), endPoint.Sub(StartPoint));
            }
            else
            {
                if (result.Option!=null && result.Option.Trim().ToUpper() =="A")
                {
                    var result2 = await this.PointInputer.Execute("请选择弧形坡道圆心:");
                    if (result2 == null || result2.ValueX == null || !(result2.ValueX is Vector2) || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    CenterPoint = result2.ValueX as Vector2;
                    var arc = new Arc2d();
                    arc.Center = new Vector2();
                    var result3 = await this.PointInputer.Execute("请选择弧形坡道起点:");
                    if (result3 == null || result3.ValueX == null || !(result3.ValueX is Vector2) || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    StartPoint = result3.ValueX as Vector2;
                    _rampInstance.Position.Set(CenterPoint.X, CenterPoint.Y);
                    arc.Radius = StartPoint.DistanceTo(CenterPoint);
                    arc.StartAngle = StartPoint.Clone().Sub(CenterPoint).Angle();
                    _rampInstance.BaseLine = arc;
                Change:
                    var result4 = await this.PointInputer.Execute("请选择坡道终点或[切换方向(C)/放弃(U)]:");
                    if (result4 == null || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    if (result4.ValueX != null && result4.ValueX is Vector2)
                    {
                        var endPoint = result4.ValueX as Vector2;
                        arc.EndAngle = endPoint.Clone().Sub(CenterPoint).Angle();
                    }
                    else
                    {
                        if (result4.Option!=null && result4.Option.Trim().ToUpper()=="C")
                        {
                            arc.IsClockwise = !arc.IsClockwise;
                            goto Change;
                        }
                        goto End;
                    }

                }
                else
                {
                    goto End;
                }
            }
            this.DrawRamp(_rampInstance);

        End:
            this.PointInputer = null;
            this.EndCreating();
            this._rampInstance = null;
            this.StartPoint = null;
            this.CenterPoint = null;
        }
     
        public override void Draw(SKCanvas canvas, LcElement element, Vector2 offset)
        {
            var rampInstance = element as QdRampInstance;
            var curves = rampInstance.Curves?.FirstOrDefault().Curve2ds;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            DrawRampCurves(canvas, curves, pen);
        }
        public override void Draw(SKCanvas canvas, LcElement element, Matrix3 matrix)
        {
            var rampInstance = element as QdRampInstance;
            // var curves = rampInstance.ShapeCurves;
            var curves = rampInstance.Curves?.FirstOrDefault().Curve2ds;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            DrawRampCurves(canvas,curves,pen);
        }
        public void DrawRampCurves(SKCanvas canvas, List<Curve2d> curves, SKPaint pen)
        {
            foreach (var curve in curves)
            {
                switch (curve.Type)
                {
                    case Curve2dType.Arc2d:
                        {
                            var arc = curve as Arc2d;
                            Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                            this.vportRt.DrawArc(arc, matrix,canvas, pen);
                            break;
                        }
                    case Curve2dType.Line2d:
                        {
                            var line = curve as Line2d;
                            Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                            var start = this.vportRt.ConvertWcsToScr(line.Start).ToSKPoint();
                            var end = this.vportRt.ConvertWcsToScr(line.End).ToSKPoint();
                            canvas.DrawLine(start,end,pen);
                            break;
                        }
                    default:
                        break;
                }
            }

        }
         private void DrawRamp(QdRampInstance rampInstance) 
        {
            if (rampInstance == null)
                return;
            rampInstance.Initilize(this.docRt.Document);
            this.docRt.Document.ModelSpace.InsertElement(rampInstance);
        }
        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
        }
        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (_rampInstance == null || _rampInstance.BaseLine == null)
            {
                return;
            }
            var mp = this.vportRt.PointerMovedPosition.ToVector2d();
            var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
            if (_rampInstance.BaseLine is Line2d line)
            {
                line.End = wcs_mp.Clone().Sub(StartPoint);
                _rampInstance.BaseLine = line;
            }
            else if (_rampInstance.BaseLine is Arc2d arc)
            {
                arc.EndAngle = wcs_mp.Clone().Sub(CenterPoint).Angle();
                _rampInstance.BaseLine = arc;
            }
            _rampInstance.ResetCache();
            using (var elePen = new SKPaint { Color = SKColors.White, IsStroke = true })
            {
                DrawRampCurves(canvas, _rampInstance.Curves.FirstOrDefault().Curve2ds, elePen);
            }
        }
        #region Grip
        private string _gripName;
        private Vector2 _position;

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var rampInstance = element as QdRampInstance;
            var grips = new List<ControlGrip>();
            if (rampInstance.BaseLine is Line2d line)
            {
                var startPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "StartPoint",
                    Position = line.Start.Clone().Add(rampInstance.Position.ToVector2())
                };
                grips.Add(startPoint);
                var endPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "EndPoint",
                    Position = line.End.Clone().Add(rampInstance.Position.ToVector2())
                };
                grips.Add(endPoint);
                var centerPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "CenterPoint",
                    Position = line.GetPoints(2)[1].Add(rampInstance.Position.ToVector2())
                };
                grips.Add(centerPoint);

            }
            else if (rampInstance.BaseLine is Arc2d arc)
            {
                var points = arc.GetPoints(1);
                var startPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "StartPoint",
                    Position = points.First().Add(rampInstance.Position.ToVector2())
                };
                grips.Add(startPoint);
                var endPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "EndPoint",
                    Position = points.Last().Add(rampInstance.Position.ToVector2()) 
                };
                grips.Add(endPoint);
                var centerPoint = new ControlGrip
                {
                    Element = rampInstance,
                    Name = "CenterPoint",
                    Position = rampInstance.Position.ToVector2()
                };
                grips.Add(centerPoint);
            }
            return grips.ToArray();
        }

        public override void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd)
        {
            var rampInstance = element as QdRampInstance;
            _rampInstance = rampInstance;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
                rampInstance.OnPropertyChangedAfter("BaseLine", null, null);
                if (gripName == "CenterPoint")
                {
                    if (rampInstance.BaseLine is Line2d line)
                    {
                        position  = position.Sub(line.GetPoints(2)[1]);
                    }
                    rampInstance.Position.Set(position.X, position.Y);
                    rampInstance.ResetCache();
                }
                if (gripName == "StartPoint")
                {
                    if (rampInstance.BaseLine is Line2d line)
                    {
                        var offset = position.Clone().Sub(line.Start.Clone().Add(rampInstance.Position.ToVector2()));
                        line.Start.Add(offset);
                        rampInstance.ResetCache();
                    }
                    else if (rampInstance.BaseLine is Arc2d arc)
                    {
                        var sp = position.Sub(rampInstance.Position.ToVector2()).Angle();
                        arc.StartAngle = sp;
                        rampInstance.ResetCache();
                    }
                }
                if (gripName == "EndPoint")
                {
                    if (rampInstance.BaseLine is Line2d line)
                    {
                        var offset = position.Clone().Sub(line.End.Clone().Add(rampInstance.Position.ToVector2()));
                        line.End.Add(offset);
                        rampInstance.ResetCache();
                    }
                    else if (rampInstance.BaseLine is Arc2d arc)
                    {
                        var ep = position.Sub(rampInstance.Position.ToVector2()).Angle();
                        arc.EndAngle = ep;
                        rampInstance.ResetCache();
                    }
                }
                rampInstance.OnPropertyChangedAfter("BaseLine", null, null);
            }
            else
            {
            }

        }
        public override List<PropertyObserver> GetPropertyObservers()
        {
            //return base.GetPropertyObservers();
            var list = new List<PropertyObserver>() {
             new PropertyObserver()
            {
                Name = "SlabThickness",
                DisplayName = "坡道板厚",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("SlabThickness"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var slabThickness)||slabThickness<0)
                        return;
                    ramp.OnPropertyChangedBefore("SlabThickness",ramp.Parameters.GetValue<double>("SlabThickness"),slabThickness);
                    ramp.Parameters.SetValue("SlabThickness",slabThickness );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("SlabThickness",ramp.Parameters.GetValue<double>("SlabThickness"),slabThickness);
                }
            },
              new PropertyObserver()
            {
                Name = "RampWidth",
                DisplayName = "坡道宽度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("RampWidth"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var rampWidth)||rampWidth<0)
                        return;
                    ramp.OnPropertyChangedBefore("RampWidth",ramp.Parameters.GetValue<double>("RampWidth"),rampWidth);
                    ramp.Parameters.SetValue("RampWidth",rampWidth );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("RampWidth",ramp.Parameters.GetValue<double>("RampWidth"),rampWidth);
                }
            },
               new PropertyObserver()
            {
                Name = "ElevationStart",
                DisplayName = "起始标高",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("ElevationStart"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var elevationStart)||elevationStart<0)
                        return;
                    ramp.OnPropertyChangedBefore("ElevationStart",ramp.Parameters.GetValue<double>("ElevationStart"),elevationStart);
                    ramp.Parameters.SetValue("ElevationStart",elevationStart );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("ElevationStart",ramp.Parameters.GetValue<double>("ElevationStart"),elevationStart);
                }
            },
               new PropertyObserver()
            {
                Name = "ElevationEnd",
                DisplayName = "结束标高",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("ElevationEnd"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var elevationEnd)||elevationEnd<0)
                        return;
                    ramp.OnPropertyChangedBefore("ElevationEnd",ramp.Parameters.GetValue<double>("ElevationEnd"),elevationEnd);
                    ramp.Parameters.SetValue("ElevationEnd",elevationEnd );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("ElevationEnd",ramp.Parameters.GetValue<double>("ElevationEnd"),elevationEnd);
                }
            }, new PropertyObserver()
            {
                Name = "Superelevation",
                DisplayName = "曲线超高",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("Superelevation"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var superelevation)||superelevation<0)
                        return;
                    ramp.OnPropertyChangedBefore("Superelevation",ramp.Parameters.GetValue<double>("Superelevation"),superelevation);
                    ramp.Parameters.SetValue("Superelevation",superelevation );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("Superelevation",ramp.Parameters.GetValue<double>("Superelevation"),superelevation);
                }
            }, new PropertyObserver()
            {
                Name = "GentleSlope",
                DisplayName = "缓坡长度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdRampInstance).Parameters.GetValue<double>("GentleSlope"),
                Setter = (ele, value) =>
                {
                    var ramp = (ele as QdRampInstance);
                    if (!double.TryParse(value.ToString(),out var gentleSlope)||gentleSlope<0)
                        return;
                    ramp.OnPropertyChangedBefore("GentleSlope",ramp.Parameters.GetValue<double>("GentleSlope"),gentleSlope);
                    ramp.Parameters.SetValue("GentleSlope",gentleSlope );
                    ramp.ResetCache();
                    ramp.OnPropertyChangedAfter("GentleSlope",ramp.Parameters.GetValue<double>("GentleSlope"),gentleSlope);
                }
            },
            };
            return list;
        }
        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_rampInstance == null)
                return;
            if (_gripName == "InsertPoint")
            {
                var offset = _position - _rampInstance.Position.ToVector2();
              //  var matrix = Matrix3.GetTranslate(offset) * _rampInstance.Matrix;
                var curves = _rampInstance.Curves?.FirstOrDefault().Curve2ds;
                var pen = this.GetDrawPen(_rampInstance);
                if (pen == LightCAD.Runtime.Constants.defaultPen)
                {
                    pen = new SKPaint { Color = SKColors.White, IsStroke = true };
                }
                DrawRampCurves(canvas, curves, pen);
            }

        }
        #endregion

    }
    public class Ramp3dAction : ComponentInstance3dAction
    {
        public override List<Object3D> Render(IComponentInstance cptIns)
        {
            try
            {
                var ramp = cptIns as QdRampInstance;
                var result = new List<Object3D>();
                var lcMats = ramp.Definition.Solid3dProvider.AssignMaterialsFunc(cptIns, null);
                if (ramp.BaseLine!=null)
                {
                    BufferGeometry geo;
                    var material = RenderMaterialManager.GetRenderMaterial(lcMats[0].Uuid);
                    if (ramp.BaseLine is Line2d line)
                    {
                        geo = CreateLineRamp(ramp,line);
                    }
                    else 
                    {
                        var arc = ramp.BaseLine as Arc2d;
                        geo = CreateArcRamp(ramp,arc);
                    }
                    material = RenderMaterialManager.GetRenderMaterial(MaterialManager.DefaultUuid);
                    var mesh = new Mesh(geo, material);
                    mesh.position.Set(ramp.Transform3d.Position.X, ramp.Transform3d.Position.Y, 0);
                    result.Add(mesh);
                } 
                return result;
            }
            catch (Exception ex)
            {

            }
            return null;
        }
        private BufferGeometry CreateLineRamp(QdRampInstance ramp, Line2d line)
        {
            var posArr = new ListEx<double>();
            var idxArr = new ListEx<int>();
            int idxOffset = 0;

            var startP = line.Start.ToVector3();     
            var rampHeight = ramp.ElevationEnd - ramp.ElevationStart;
            var endP = line.End.ToVector3(rampHeight);
            //var gslope = rampHeight / line.Length / 2;
            //var gsHight = gslope * ramp.GentleSlope;
            var gsHight = rampHeight * ramp.GentleSlope / 2 / (line.Length- ramp.GentleSlope);
            rampHeight -= gsHight * 2;
            var baseVec = endP.Clone().Sub(startP).Normalize();
            var vertical = new Vector3(-line.Dir.Y, line.Dir.X, 0).Normalize();
            var normal = baseVec.Clone().Cross(vertical).Normalize();
            var leftNormal = normal.Clone().Cross(baseVec);
            var surfaces = new List<Surface3d>();
            var startLine = new Line3d(startP.Clone().AddScaledVector(vertical, -ramp.RampWidth / 2), startP.Clone().AddScaledVector(vertical, ramp.RampWidth / 2));
            var endLine = new Line3d(endP.Clone().AddScaledVector(vertical, ramp.RampWidth / 2), endP.Clone().AddScaledVector(vertical, -ramp.RampWidth / 2));
            var leftLoop = new List<Curve3d>();
            if (ramp.GentleSlope==0)
            {
                var leftLine = new Line3d(startP.Clone().AddScaledVector(vertical, ramp.RampWidth / 2), endP.Clone().AddScaledVector(vertical, ramp.RampWidth / 2));
                var rightLine = new Line3d(endP.Clone().AddScaledVector(vertical, -ramp.RampWidth / 2), startP.Clone().AddScaledVector(vertical, -ramp.RampWidth / 2));
                var outloop = new List<Curve3d>();
                outloop.Add(leftLine.Clone());
                outloop.Add(endLine.Clone());
                outloop.Add(rightLine.Clone());
                outloop.Add(startLine.Clone());
                var planarTop = new PlanarSurface3d(new Plane(normal), outloop);
                var planerBottom = new PlanarSurface3d(new Plane(normal.Clone().Negate()), outloop.Select(n => n.Clone().Translate(0, 0, -ramp.SlabThickness)).ToList());
                leftLoop.Add(leftLine.Clone());
                leftLoop.Add(new Line3d(leftLine.End.Clone(), leftLine.End.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness))));
                leftLoop.Add(leftLine.Clone().Reverse().Translate(0, 0, -ramp.SlabThickness));
                leftLoop.Add(new Line3d(leftLine.Start.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness)), leftLine.Start.Clone()));
                surfaces.Add(planarTop);
                surfaces.Add(planerBottom);
            }
            else
            {
                var div = 32;
                var gsVec = new Vector2(ramp.GentleSlope, gsHight);
                var angle = gsHight > 0?gsVec.Angle():Utils.TwoPI- gsVec.Angle();
                var radian = Math.Atan(Math.Tan(angle) * 2);
                var radius = gsVec.Length() / 2 * Math.Tan((Math.PI- radian+angle)/2);
                var arc = new Arc2d();
                arc.Radius = radius;
                arc.IsClockwise = gsHight < 0;
                if (gsHight > 0)
                {
                    arc.Center = new Vector2(gsVec.X / 2 - Math.Sin(angle) * radius, gsVec.Y / 2 + Math.Cos(angle) * radius);
                    arc.StartAngle = (gsVec / 2).Sub(arc.Center).Angle();
                    arc.EndAngle = arc.StartAngle + radian - angle;
                }
                else
                {
                    arc.Center = new Vector2(gsVec.X / 2 - Math.Sin(angle) * radius, gsVec.Y / 2 - Math.Cos(angle) * radius);
                    arc.StartAngle = (gsVec / 2).Sub(arc.Center).Angle();
                    arc.EndAngle = arc.StartAngle - radian + angle;
                }
                var arcp = arc.GetPoints(div);
                var matrix = new Matrix4();
                matrix.MakeBasis(line.Dir.ToVector3().Normalize(),new Vector3(0,0,1), line.Dir.ToVector3().Cross(new Vector3(0, 0, 1)).Normalize());
                var offset = line.Dir.Clone().MultiplyScalar(ramp.GentleSlope).ToVector3(gsHight);
                leftLoop.Add(new Line3d(startLine.End.Clone(), startLine.End.Clone().Add(offset/2)));
                for (var i = 0; i < div; i++)
                {
                    var sp = arcp[i].Clone().ToVector3().ApplyMatrix4(matrix);
                    var ep = arcp[i+1].Clone().ToVector3().ApplyMatrix4(matrix);
                    leftLoop.Add(new Line3d(sp.AddScaledVector(vertical, ramp.RampWidth / 2), ep.AddScaledVector(vertical, ramp.RampWidth / 2)));
                }
                var centerOffset = line.Dir.Clone().MultiplyScalar(line.Length - ramp.GentleSlope*2).ToVector3(rampHeight);
                leftLoop.Add(new Line3d(startLine.End.Clone().Add(offset).Add(centerOffset * (offset.Length() /2 / centerOffset.Length())), startLine.End.Clone().Add(offset).Add(centerOffset * (1 - offset.Length() / centerOffset.Length()))));
                var endArc = new Arc2d();
                endArc.Radius = arc.Radius;
                endArc.Center = arc.Center.Clone().AddScaledVector(gsVec.Clone().Sub(arc.Center).Normalize(), gsVec.Clone().Sub(arc.Center).Length() * 2).Add(new Vector2(line.Length - ramp.GentleSlope * 2,rampHeight));
                endArc.StartAngle = arc.EndAngle - Math.PI;
                endArc.EndAngle = arc.StartAngle - Math.PI;
                endArc.IsClockwise = !arc.IsClockwise;
                var earcp = endArc.GetPoints(div);
                for (var i = 0; i < div; i++)
                {
                    var sp = earcp[i].Clone().ToVector3().ApplyMatrix4(matrix);
                    var ep = earcp[i + 1].Clone().ToVector3().ApplyMatrix4(matrix);
                    leftLoop.Add(new Line3d(sp.AddScaledVector(vertical, ramp.RampWidth / 2), ep.AddScaledVector(vertical, ramp.RampWidth / 2)));
                }
                leftLoop.Add(new Line3d(leftLoop.Last().End.Clone(), endLine.Start.Clone()));
                var topPosArr = new List<Vector3>();
                var bottomPosArr = new List<Vector3>();
                var topIdx = new ListEx<int>();
                var bottomIdx = new ListEx<int>();
                foreach (Line3d lfl in leftLoop)
                {
                    topIdx.Push(topPosArr.Count + 0, topPosArr.Count + 1, topPosArr.Count + 2, topPosArr.Count + 1, topPosArr.Count + 3, topPosArr.Count + 2);
                    bottomIdx.Push(topPosArr.Count + 0, topPosArr.Count + 2, topPosArr.Count + 1, topPosArr.Count + 2, topPosArr.Count + 3, topPosArr.Count + 1);
                    topPosArr.Add(lfl.Start.Clone());
                    topPosArr.Add(lfl.Start.Clone().Add(vertical.Clone().Negate().MulScalar(ramp.RampWidth)));
                }
                topPosArr.Add(leftLoop.Last().End.Clone());
                topPosArr.Add(leftLoop.Last().End.Clone().Add(vertical.Clone().Negate().MulScalar(ramp.RampWidth)));
                bottomPosArr = topPosArr.Select(n=>n.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness))).ToList();
                posArr.AddRange(topPosArr.SelectMany(n => n.ToArray()));
                idxArr.AddRange(topIdx);
                posArr.AddRange(bottomPosArr.SelectMany(n => n.ToArray()));
                idxArr.AddRange(bottomIdx.Select(idx => idx + topPosArr.Count));
                var bls = leftLoop.Select(n => n.Clone().Reverse().Translate(0, 0, -ramp.SlabThickness)).ToList();
                bls.Reverse();
                leftLoop.Add(new Line3d(leftLoop.Last().End.Clone(), bls.First().Start.Clone()));
                leftLoop.AddRange(bls);
                leftLoop.Add(new Line3d(startLine.End.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness)), startLine.End.Clone()));

            }
            var planerLeft = new PlanarSurface3d(new Plane(leftNormal), leftLoop);
            var planerRight = new PlanarSurface3d(new Plane(leftNormal.Clone().Negate()), leftLoop.Select(n => n.Clone().Translate(vertical.Clone().Negate().MulScalar(ramp.RampWidth))).ToList());
            surfaces.Add(planerLeft);
            surfaces.Add(planerRight);
            var startLoop = new List<Curve3d>();
            startLoop.Add(startLine.Clone());
            startLoop.Add(new Line3d(startLine.End.Clone(), startLine.End.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness))));
            startLoop.Add(startLine.Clone().Reverse().Translate(0, 0, -ramp.SlabThickness));
            startLoop.Add(new Line3d(startLine.Start.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness)), startLine.Start.Clone()));
            var planerStart = new PlanarSurface3d(new Plane(vertical.Clone().Cross(new Vector3(0, 0, -1))), startLoop);
            var planerEnd = new PlanarSurface3d(new Plane(planerStart.Normal.Clone().Negate()), startLoop.Select(n => n.Clone().Translate(endP.Clone().Sub(startP))).ToList());
            surfaces.Add(planerStart);
            surfaces.Add(planerEnd);
            for (int i = 0; i < surfaces.Count; i++)
            {
                var face = surfaces[i];
                var tuple = face.Trianglate();
                idxOffset = posArr.Length / 3;
                posArr.AddRange(tuple.Item1);
                idxArr.AddRange(tuple.Item2.Select(idx => idx + idxOffset));
            }
            GeometryData geometryData = new GeometryData();
            geometryData.Position = posArr;
            geometryData.Index = idxArr;
            var geo = geometryData.GetBufferGeometry();
            geo.name = "Ramp";
            geo.translate(0,0,ramp.ElevationStart);
            geo.computeVertexNormals();
            geo.SetUV();
            return geo;
        }
        private BufferGeometry CreateArcRamp(QdRampInstance ramp, Arc2d arc)
        {
            var div = 32;
            var lArc = arc.Clone() as Arc2d;
            lArc.Radius = arc.Radius - ramp.RampWidth / 2;
            var rArc = arc.Clone() as Arc2d;
            rArc.Radius = arc.Radius + ramp.RampWidth / 2;
            var allSweepAngel = Utils.AngelNormalize(arc.IsClockwise ? arc.StartAngle - arc.EndAngle : arc.EndAngle - arc.StartAngle);
            var arcLen = arc.Radius * allSweepAngel;
            var rampHeight = ramp.ElevationEnd - ramp.ElevationStart;
            //var gslope = rampHeight / arcLen / 2;
            //var gsHight = gslope * ramp.GentleSlope;
            var gsHight = rampHeight * ramp.GentleSlope / 2 / (arcLen - ramp.GentleSlope);
            rampHeight -= gsHight * 2;
            var gsSweep = ramp.GentleSlope / arcLen * allSweepAngel;
            var gsLArc = lArc.Clone() as Arc2d;
            var gsRArc = rArc.Clone() as Arc2d;
            var geLArc = lArc.Clone() as Arc2d;
            var geRArc = rArc.Clone() as Arc2d;
            if (arc.IsClockwise)
            {
                gsLArc.EndAngle = arc.StartAngle - gsSweep;
                gsRArc.EndAngle = arc.StartAngle - gsSweep;
                geLArc.StartAngle = arc.EndAngle + gsSweep;
                geRArc.StartAngle = arc.EndAngle + gsSweep;
                lArc.StartAngle -= gsSweep;
                lArc.EndAngle += gsSweep;
                rArc.StartAngle -= gsSweep;
                rArc.EndAngle += gsSweep;
            }
            else
            {
                gsLArc.EndAngle = arc.StartAngle + gsSweep;
                gsRArc.EndAngle = arc.StartAngle + gsSweep;
                geLArc.StartAngle = arc.EndAngle - gsSweep;
                geRArc.StartAngle = arc.EndAngle - gsSweep;
                lArc.StartAngle += gsSweep;
                lArc.EndAngle -= gsSweep;
                rArc.StartAngle += gsSweep;
                rArc.EndAngle -= gsSweep;
            }
            var gsLen = Math.Sqrt(Math.Pow(ramp.GentleSlope, 2) + Math.Pow(gsHight, 2));
            var rampLen = Math.Sqrt(Math.Pow(arcLen - ramp.GentleSlope * 2, 2) + Math.Pow(rampHeight, 2));
            var lPoints = lArc.GetPoints(div);
            var rPoints = rArc.GetPoints(div);
            var gslPoints = gsLArc.GetPoints(div);
            var gsrPoints = gsRArc.GetPoints(div);
            var gelPoints = geLArc.GetPoints(div);
            var gerPoints = geRArc.GetPoints(div);
            var postions = new ListEx<double>();
            var idxArr = new ListEx<int>();
            var leftPos = new ListEx<double>();
            var rightPos = new ListEx<double>();
            var topPos = new ListEx<double>();
            var bottomPos = new ListEx<double>();
            var startPos = new ListEx<double>();
            var endPos = new ListEx<double>();
            var leftIdxArr = new ListEx<int>();
            var rightIdxArr = new ListEx<int>();
            var topIdxArr = new ListEx<int>();
            var bottomIdxArr = new ListEx<int>();
            var startIdxArr = new ListEx<int>();
            var endIdxArr = new ListEx<int>();
            if (ramp.GentleSlope > 0)
            {
                for (var i = 0; i < div; i++)
                {
                    var lp = gslPoints[i].ToVector3(i * 1.0 / div * gsHight + ramp.ElevationStart);
                    var rp = gsrPoints[i].ToVector3(i * 1.0 / div * gsHight + ramp.ElevationStart);
                    var lpb = lp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                    var rpb = rp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                    //var offset = - Math.Sin(Math.PI * i / div) * ramp.Superelevation * ramp.GentleSlope/ arcLen;
                    var offset = -Math.Sin(Math.PI * i / div) * gsHight * gsLen / rampLen;
                    rp.Add(new Vector3(0, 0, offset));
                    rpb.Add(new Vector3(0, 0, offset));
                    lp.Add(new Vector3(0, 0, offset));
                    lpb.Add(new Vector3(0, 0, offset));
                    leftPos.Push(lp.ToArray());
                    leftPos.Push(lpb.ToArray());
                    rightPos.Push(rp.ToArray());
                    rightPos.Push(rpb.ToArray());
                    topPos.Push(lp.ToArray());
                    topPos.Push(rp.ToArray());
                    bottomPos.Push(lpb.ToArray());
                    bottomPos.Push(rpb.ToArray());
                    if (i == 0)
                    {
                        startPos.Push(lp.ToArray());
                        startPos.Push(lpb.ToArray());
                        startPos.Push(rp.ToArray());
                        startPos.Push(rpb.ToArray());
                        startIdxArr.Push(0, 1, 2, 2, 1, 3);
                    }
                    leftIdxArr.Push(i * 2, (i + 1) * 2, i * 2 + 1, (i + 1) * 2, (i + 1) * 2 + 1, i * 2 + 1);
                    rightIdxArr.Push(i * 2, i * 2 + 1, (i + 1) * 2, (i + 1) * 2, i * 2 + 1, (i + 1) * 2 + 1);
                    topIdxArr.Push(i * 2, i * 2 + 1, (i + 1) * 2, (i + 1) * 2, i * 2 + 1, (i + 1) * 2 + 1);
                    bottomIdxArr.Push(i * 2, (i + 1) * 2, i * 2 + 1, (i + 1) * 2, (i + 1) * 2 + 1, i * 2 + 1);
                }
            }
            var idxOffset = leftPos.Length / 3;
            for (var i = 0; i <= div; i++)
            {
                var lp = lPoints[i].ToVector3(i * 1.0 / div * rampHeight + gsHight + ramp.ElevationStart);
                var rp = rPoints[i].ToVector3(i * 1.0 / div * rampHeight + gsHight + ramp.ElevationStart);
                var lpb = lp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                var rpb = rp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                var offset = Math.Sin(Math.PI*i/div)*ramp.Superelevation;
                rp.Add(new Vector3(0, 0, offset));
                rpb.Add(new Vector3(0, 0, offset));
                //postions.Push(lp.ToArray());
                //postions.Push(lpb.ToArray());
                //postions.Push(rp.ToArray());
                //postions.Push(rpb.ToArray());
                //var count = postions.Count/3;
                //if (i==0)
                //{
                //    idxArr.Push(0, 1, 2, 2, 1, 3);
                //}
                //else if (i == div)
                //{
                //    idxArr.Push(count - 4, count - 2, count - 3, count - 2, count - 1, count - 3);
                //    continue;
                //}
                //idxArr.Push(count - 4, count, count - 3, count, count + 1, count - 3);//left
                //idxArr.Push(count + 2, count - 2, count - 1, count + 2, count - 1, count + 3);//right
                //idxArr.Push(count + 2, count, count - 4, count + 2, count - 4, count - 2);//top
                //idxArr.Push(count + 1, count + 3, count - 1, count + 1, count - 1, count - 3);//bottom

                leftPos.Push(lp.ToArray());
                leftPos.Push(lpb.ToArray());
                rightPos.Push(rp.ToArray());
                rightPos.Push(rpb.ToArray());
                topPos.Push(lp.ToArray());
                topPos.Push(rp.ToArray());
                bottomPos.Push(lpb.ToArray());
                bottomPos.Push(rpb.ToArray());
                if (i == 0 && ramp.GentleSlope == 0)
                {
                    startPos.Push(lp.ToArray());
                    startPos.Push(lpb.ToArray());
                    startPos.Push(rp.ToArray());
                    startPos.Push(rpb.ToArray());
                    startIdxArr.Push(0, 1, 2, 2, 1, 3);
                }
                else if (i == div && ramp.GentleSlope == 0)
                {
                    endPos.Push(lp.ToArray());
                    endPos.Push(lpb.ToArray());
                    endPos.Push(rp.ToArray());
                    endPos.Push(rpb.ToArray());
                    endIdxArr.Push(0, 2, 1, 2, 3, 1);
                    continue;
                }
                leftIdxArr.Push(i * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + 1 + idxOffset, i * 2 + 1 + idxOffset);
                rightIdxArr.Push(i * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + 1 + idxOffset);
                topIdxArr.Push(i * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + 1 + idxOffset);
                bottomIdxArr.Push(i * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + 1 + idxOffset, i * 2 + 1 + idxOffset);
            }
            if (ramp.GentleSlope > 0)
            {
                idxOffset = leftPos.Length / 3 - 2;
                for (var i = 1; i <= div; i++)
                {
                    var lp = gelPoints[i].ToVector3(i * 1.0 / div * gsHight + ramp.ElevationEnd - gsHight);
                    var rp = gerPoints[i].ToVector3(i * 1.0 / div * gsHight + ramp.ElevationEnd - gsHight);
                    var lpb = lp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                    var rpb = rp.Clone().Add(new Vector3(0, 0, -ramp.SlabThickness));
                    var offset = Math.Sin(Math.PI * i / div) * gsHight * gsLen / rampLen;
                    rp.Add(new Vector3(0, 0, offset));
                    rpb.Add(new Vector3(0, 0, offset));
                    lp.Add(new Vector3(0, 0, offset));
                    lpb.Add(new Vector3(0, 0, offset));
                    leftPos.Push(lp.ToArray());
                    leftPos.Push(lpb.ToArray());
                    rightPos.Push(rp.ToArray());
                    rightPos.Push(rpb.ToArray());
                    topPos.Push(lp.ToArray());
                    topPos.Push(rp.ToArray());
                    bottomPos.Push(lpb.ToArray());
                    bottomPos.Push(rpb.ToArray());
                    if (i == div)
                    {
                        endPos.Push(lp.ToArray());
                        endPos.Push(lpb.ToArray());
                        endPos.Push(rp.ToArray());
                        endPos.Push(rpb.ToArray());
                        endIdxArr.Push(0, 2, 1, 2, 3, 1);
                        continue;
                    }
                    leftIdxArr.Push(i * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + 1 + idxOffset, i * 2 + 1 + idxOffset);
                    rightIdxArr.Push(i * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + 1 + idxOffset);
                    topIdxArr.Push(i * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + 1 + idxOffset);
                    bottomIdxArr.Push(i * 2 + idxOffset, (i + 1) * 2 + idxOffset, i * 2 + 1 + idxOffset, (i + 1) * 2 + idxOffset, (i + 1) * 2 + 1 + idxOffset, i * 2 + 1 + idxOffset);
                }
            }
            idxArr.AddRange(startIdxArr);
            postions.AddRange(startPos);
            idxArr.AddRange(endIdxArr.Select(n => n += postions.Length / 3));
            postions.AddRange(endPos);
            idxArr.AddRange(leftIdxArr.Select(n => n += postions.Length / 3));
            postions.AddRange(leftPos);
            idxArr.AddRange(rightIdxArr.Select(n => n += postions.Length / 3));
            postions.AddRange(rightPos);
            idxArr.AddRange(topIdxArr.Select(n => n += postions.Length / 3));
            postions.AddRange(topPos);
            idxArr.AddRange(bottomIdxArr.Select(n => n += postions.Length / 3));
            postions.AddRange(bottomPos);
            GeometryData geometryData = new GeometryData();
            geometryData.Position = postions;
            geometryData.Index = idxArr;
            var geo = geometryData.GetBufferGeometry();
            geo.name = "Ramp";
            geo.computeVertexNormals();
            geo.SetUV();
            return geo;
        }
    }
}
